// is 自定义类型谓词
function isNumber(x: any): x is number {
  return typeof x === "number";
}

function isString(x: any): x is string {
  return typeof x === "string";
}

interface Vehicle {
  move: (distance: number) => void;
}

class Car implements Vehicle {
  move = (distance: number) => {
    // Move car…
    console.log(distance);
  };

  turnSteeringWheel = (direction: string) => {
    // Turn wheel…
    console.log(direction);
  };
}

class VehicleController {
  constructor(public vehicle: Vehicle) {
    this.vehicle = vehicle;
  }
}

const car = new Car();
const vehicleController = new VehicleController(car);
let { vehicle } = vehicleController;
// vehicle.turnSteeringWheel("left"); // 报错：类型“Vehicle”上不存在属性“turnSteeringWheel”
if (vehicle instanceof Car) {
  // instanceof有一定限制，只能对类生效，如果是对象【不是某个类的实例】就无法判断
  vehicle.turnSteeringWheel("left");
}

/* 尽管 anotherCar 跟前面已经定义的 car 拥有相同的形状，但它并不是 Car 汽车类的实例，因此在这种情况下，anotherVehicle instanceof Car 表达式返回的结果为 false */
const anotherCar = {
  move: (distance: number) => null,
  turnSteeringWheel: (direction: string) => null
};

const anotherVehicleController = new VehicleController(anotherCar);
const { vehicle: anotherVehicle } = anotherVehicleController;

if (anotherVehicle instanceof Car) { // 用instanceof做类型判断就会出问题，结果判断anotherVehicle不是一辆车
  anotherVehicle.turnSteeringWheel('left');
  console.log("这是一辆车");
} else {
  console.log("这不是一辆车");
}

// 使用is自定义类型保护，定义一个自定义类型保护函数 isCar
// 可以传递任何值给 isCar 函数，用来判断它是不是一辆车
// isCar 函数与普通函数的最大区别是，该函数的返回类型是 vehicle is Car，这就是 "类型谓词"
function isCar(vehicle: any): vehicle is Car {
  return (vehicle as Car).turnSteeringWheel !== undefined;
}
if (isCar(anotherCar)) {
  anotherCar.turnSteeringWheel('left');
  console.log("使用类型谓词进行判断【isCar函数】，这是一辆车");
} else {
  console.log("这不是一辆车");
}

/* 
自定义类型保护的主要特点：
1. 返回类型谓词，如 vehicle is Car
2. 包含可以准确确定给定变量类型的逻辑语句，如 vehicle.turnSteeringWheel !== undefined
*/

// 如果检查的类型很多，需要使用范型进行类型约束
function isOfType<T>(
  varToBeChecked: any,
  propertyToCheckFor: keyof T
): varToBeChecked is T {
  return (varToBeChecked as T)[propertyToCheckFor] !== undefined;
}
if (isOfType<Car>(anotherVehicle, 'turnSteeringWheel')) {
  anotherCar.turnSteeringWheel('left');
  console.log("使用范型进行类型判断，这是一辆车");
} else {
  console.log("这不是一辆车");
}


export {};
